package com.jcompressor.faces.listener;

import java.util.ArrayList;
import java.util.List;

import javax.faces.application.ResourceDependencies;
import javax.faces.application.ResourceDependency;
import javax.faces.component.UIComponent;
import javax.faces.component.UIOutput;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.AbortProcessingException;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;
import javax.faces.event.PreRenderViewEvent;
import javax.faces.event.SystemEvent;
import javax.faces.event.SystemEventListener;

import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;

import com.jcompressor.JcompressorContext;
import com.jcompressor.annotations.JcompressorResource;
import com.jcompressor.annotations.JcompressorResources;
import com.jcompressor.config.Configurator;
import com.jcompressor.faces.annotations.FacesAnnotationScanner;
import com.jcompressor.faces.component.UIResource;
import com.jcompressor.faces.utils.FacesUtils;
import com.jcompressor.model.config.Resource;
import com.jcompressor.model.resources.Template;
import com.jcompressor.type.ResourceType;

public class ResourceListener implements PhaseListener, SystemEventListener {
	
	private static final long serialVersionUID = -6311818777729077494L;
	
	@Override
	public void processEvent(final SystemEvent event) throws AbortProcessingException {
		final FacesContext facesContext = FacesContext.getCurrentInstance();
		final UIViewRoot viewRoot = facesContext.getViewRoot();
		final List<UIComponent> resourcesToRemove = new ArrayList<UIComponent>();
		
		if (!facesContext.isPostback()) {
			final String viewId = viewRoot.getViewId();
			facesContext.getExternalContext().getSessionMap().put("JCOMPRESSOR_FACES_VIEW_ID", viewId);
			
			for (final UIComponent component : viewRoot.getComponentResources(facesContext, "head")) {
				if (ObjectUtils.equals(component.getClass(), UIOutput.class)) {
					final UIOutput output = (UIOutput)component;
					if (StringUtils.equals("javax.faces.resource.Stylesheet", output.getRendererType()) || StringUtils.equals("javax.faces.resource.Script", output.getRendererType())) {
						resourcesToRemove.add(component);
					}
				}
			}
			
			boolean hasScripts = false;
			boolean hasStyles = false;
			
			for (final UIComponent component : resourcesToRemove) {
				viewRoot.getComponentResources(facesContext, "head").remove(component);
				
				final String name = component.getAttributes().get("name").toString();
				if (!hasScripts && StringUtils.endsWith(name, ".js")) {
					hasScripts = true;
				}
				
				if (!hasStyles && StringUtils.endsWith(name, ".css")) {
					hasStyles = true;
				}
			}
			
			if (hasScripts) {
				final UIResource resource = new UIResource();
				resource.getAttributes().put("type", ResourceType.SCRIPT.getContentType());
				resource.getAttributes().put("path", "/jcompressor/js");
				
				for (final UIComponent component : facesContext.getViewRoot().getChildren()) {
					if ((component.getRendererType() != null) && StringUtils.equals(component.getRendererType(), "javax.faces.Head")) {
						component.getChildren().add(resource);
						break;
					}
				}
			}
			
			if (hasStyles) {
				final UIResource resource = new UIResource();
				resource.getAttributes().put("type", ResourceType.STYLE.getContentType());
				resource.getAttributes().put("path", "/jcompressor/css");
				
				for (final UIComponent component : facesContext.getViewRoot().getChildren()) {
					if ((component.getRendererType() != null) && StringUtils.equals(component.getRendererType(), "javax.faces.Head")) {
						component.getChildren().add(resource);
						break;
					}
				}
			}
			
			// TODO: Detect changes so the resources can be adjusted
			final Configurator configurator = JcompressorContext.getConfigurator();
			if (!configurator.getResourcesConfiguration().containsTemplate(viewId)) {
				final List<UIComponent> children = FacesUtils.getAllChildren(new ArrayList<UIComponent>(), facesContext.getViewRoot());
				final FacesAnnotationScanner scanner = new FacesAnnotationScanner(children, new Class[] {JcompressorResources.class, JcompressorResource.class, ResourceDependencies.class, ResourceDependency.class});
				scanner.scan();
				
				final Template template = new Template();
				template.setId(viewId);
				
				for (final Resource res : scanner.getResources()) {
					if (configurator.getResourcesConfiguration().getResource(res.getId()) != null) {
						final Resource resource = new Resource();
						resource.setId(StringUtils.isBlank(res.getId()) ? res.getName() : res.getId());
						template.addResource(resource);
					}
				}
				
				configurator.addTemplate(template);
			}
		}
	}
	
	@Override
	public boolean isListenerForSource(final Object source) {
		return (source instanceof UIViewRoot);
	}
	
	@Override
	public void afterPhase(final PhaseEvent event) {
		FacesContext.getCurrentInstance().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
	}
	
	@Override
	public void beforePhase(final PhaseEvent event) {}
	
	@Override
	public PhaseId getPhaseId() {
		return PhaseId.RESTORE_VIEW;
	}
	
}